﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Model
{
    public abstract class Algoritme
    {
        /// <summary>
        /// Voert het algoritme uit
        /// </summary>
        public abstract Winkelwagen AlgoritmeUitvoeren();

        public abstract void StopAlgoritme();
    }

    #region BesteTijd
    
    
    #endregion

    #region BesteBenaderingTijd
    public class BesteBenaderingTijd : Algoritme
    {
        private enum Richting
        {
            Boven,
            Links,
            Rechts,
            Beneden
        }

        public override Winkelwagen AlgoritmeUitvoeren()
        {
            throw new NotImplementedException();
        }

        public override void StopAlgoritme()
        {
            throw new NotImplementedException();
        }

        private Winkelwagen GeneratieAlgoritme(List<Winkelwagen> beginGeneratie, int generaties)
        {
            int teller = 0;
            do
            {
                beginGeneratie = bepaalFitheid(beginGeneratie);
                beginGeneratie = beste10(beginGeneratie);
                beginGeneratie = maakGeneratie(beginGeneratie);
                teller++;
            } while (teller < generaties);
            return beginGeneratie[0];
        }

        private List<Winkelwagen> maakGeneratie(List<Winkelwagen> oplossingen)
        {
            List<Winkelwagen> generatie = new List<Winkelwagen>();
            generatie.Add(oplossingen[oplossingen.Count - 1]);
            Winkelwagen dummy = new Winkelwagen(generatie[0].AfgelegdeWeg[0]);
            for (int i = 0; i < oplossingen.Count-2; i++)
            {
                List<Winkelwagen> tijdlijk = new List<Winkelwagen>();
                tijdlijk.AddRange(VermenigvuldigenWegen(oplossingen[i], oplossingen[i + 1]));
            }
            return generatie;
        }

        private Random rnd;

        private List<Winkelwagen> VermenigvuldigenWegen(Winkelwagen wagen1, Winkelwagen wagen2)
        {
            List<Winkelwagen> antwoord = new List<Winkelwagen>();

            

            return antwoord;
        }

        private Winkelwagen bouwOplossing(Winkelwagen wagen1, Winkelwagen wagen2)
        {
            AbstracteLaag ab = AbstracteLaag.GetInstance();
            int selectorWagen1 = rnd.Next(wagen1.AfgelegdeWeg.Count - 1);
            int selectorWagen2 = rnd.Next(wagen2.AfgelegdeWeg.Count - 1);
            Richting Wagen1enum = bepaalRichting(wagen1, selectorWagen1);
            Richting Wagen2enum = bepaalRichting(wagen2, selectorWagen2);
            Winkelwagen wagen = new Winkelwagen(wagen1.AfgelegdeWeg[0]);
            for (int i = 1; i < selectorWagen1; i++)
            {
                wagen.AfgelegdeWeg.Add(wagen1.AfgelegdeWeg[i]);
            }
            return wagen;
        }

        private Richting bepaalRichting(Winkelwagen wagen, int punt)
        {
            Point plaats = wagen.AfgelegdeWeg[punt].Plaats;
            Point daarna = wagen.AfgelegdeWeg[punt + 1].Plaats;
            int qhorizontaal = plaats.X - daarna.X;
            int qvertikaal = plaats.Y - daarna.Y;
            Richting antwoord = Richting.Boven;
            if (Math.Abs(qhorizontaal)>Math.Abs(qvertikaal))
            {
                if (qhorizontaal>0)
                {
                    antwoord = Richting.Rechts;
                }
                else
                {
                    antwoord = Richting.Links;
                }
            }
            else
            {
                if (qvertikaal>0)
                {
                    antwoord = Richting.Boven;
                }
                else
                {
                    antwoord = Richting.Beneden;
                }
            }
            return antwoord;
        }

        private List<Winkelwagen> VermenigvuldigProducten(Winkelwagen wagen1, Winkelwagen wagen2)
        {
            List<Winkelwagen> antwoord = new List<Winkelwagen>();
            return antwoord;
        }

        private List<Winkelwagen> beste10(List<Winkelwagen> beginGeneratie)
        {
            List<Winkelwagen> waarden = new List<Winkelwagen>();
            if (beginGeneratie.Count < 1024)
            {
                beginGeneratie = MergeSort(beginGeneratie);
                for (int i = 0; i < 10; i++)
                {
                    waarden.Add(beginGeneratie[i]);
                }
            }
            else
            {
                beginGeneratie = selectionSort(beginGeneratie);
                for (int i = 0; i < 10; i++)
                {
                    waarden.Add(beginGeneratie[i]);
                }
            }
            return waarden;
        }
        private List<Winkelwagen> bepaalFitheid(List<Winkelwagen> oplossingen)
        {
            for (int i = 0; i < oplossingen.Count; i++)
            {
                oplossingen[i].Fitheid = oplossingen[i].Totaalprijs * 100;
            }
            return oplossingen;
        }

        private List<Winkelwagen> MergeSort(List<Winkelwagen> array)
        {
            if (array.Count <= 1)
            {
                return array;
            }
            else
            {
                int midden = array.Count / 2;
                // Array B wordt gemaakt
                List<Winkelwagen> B = new List<Winkelwagen>();
                // Array A wordt nu gemaakt
                List<Winkelwagen> A = new List<Winkelwagen>();
                int teller;
                // De array's A en B krijgen elementen van het Array
                for (teller = 0; teller < midden; teller++)
                {
                    A.Add(array[teller]);
                }
                for (; teller < array.Count; teller++)
                {
                    B.Add(array[teller]);
                }
                A = MergeSort(A);
                B = MergeSort(B);
                array = Merge(A, B, array);
            }
            return array;
        }
        private List<Winkelwagen> Merge(List<Winkelwagen> A, List<Winkelwagen> B, List<Winkelwagen> C)
        {
            int i = 0, j = 0, k = 0;
            while (i < A.Count && j < B.Count)
            {
                if (A[i].Fitheid < B[j].Fitheid)
                {
                    C[k] = A[i];
                    i++;
                }
                else
                {
                    C[k] = B[j];
                    j++;
                }
                k++;
            }
            if (i == A.Count)
            {
                //kopieert de rest van B naar C
                while (j < B.Count)
                {
                    C[k] = B[j];
                    j++;
                    k++;
                }
            }
            else
            {
                //kopieert de rest van A naar C
                while (i < A.Count)
                {
                    C[k] = A[i];
                    i++;
                    k++;
                }
            }
            return C;
        }

        /// <summary>
        /// Algor2 selection
        /// </summary>
        /// <param name="ongesorteerd"></param>
        /// <returns></returns>
        public List<Winkelwagen> selectionSort(List<Winkelwagen> ongesorteerd)
        {
            for (int i = 0; i < ongesorteerd.Count && i < 10; i++)
            {
                int minIndex = i;
                for (int j = i + 1; j < ongesorteerd.Count; j++)
                {
                    if (ongesorteerd[j].Fitheid > ongesorteerd[minIndex].Fitheid)
                    {
                        minIndex = j;
                    }
                }
                Winkelwagen hulp = ongesorteerd[i];
                ongesorteerd[i] = ongesorteerd[minIndex];
                ongesorteerd[minIndex] = hulp;
            }
            return ongesorteerd;
        }
    }
    #endregion

    #region BesteBedrag
    public class BesteBedrag : Algoritme
    {
        public class Collector
        {
            //De beste winkelwagen tot nu toe
            private Winkelwagen bestewagen;

            //Het doelbedrag
            private double doelBedrag;

            //De loopsnelheid
            private double snelheid;

            //De fout factor in euro per seconde
            private double penalty;

            //De static Collector variabele
            private static Collector singleton;

            private double tijd;

            public Winkelwagen Bestewagen
            {
                get { return bestewagen; }
            }

            public double Tijd
            {
                get { return tijd; }
            }

            /// <summary>
            /// Private constructor
            /// </summary>
            /// <param name="doelBedrag">Het doelbedrag</param>
            /// <param name="snelheid">De snelheid</param>
            /// <param name="penalty">De penalty in euro/seconde</param>
            private Collector(Winkelwagen wagen, double doelBedrag, double snelheid, double penalty)
            {
                this.bestewagen = wagen;
                this.doelBedrag = doelBedrag;
                this.snelheid = snelheid;
                this.penalty = penalty;
            }

            /// <summary>
            /// Private constructor
            /// </summary>
            /// <param name="doelBedrag">Het doelbedrag</param>
            /// <param name="snelheid">De snelheid</param>
            /// <param name="penalty">De penalty in euro/seconde</param>
            private Collector(double doelBedrag, double snelheid, double penalty)
            {
                this.doelBedrag = doelBedrag;
                this.snelheid = snelheid;
                this.penalty = penalty;
            }

            /// <summary>
            /// Haal instantie op
            /// </summary>
            /// <param name="doelBedrag">Het doelbedrag</param>
            /// <param name="snelheid">De snelheid</param>
            /// <param name="penalty">De penalty in euro/seconde</param>
            /// <returns>Het object Collector</returns>
            public static Collector GetInstance(Winkelwagen wagen, double doelBedrag, double snelheid, double penalty)
            {
                return singleton ?? (singleton = new Collector(wagen, doelBedrag,snelheid,penalty));
            }

            /// <summary>
            /// Haal instantie op
            /// </summary>
            /// <param name="doelBedrag">Het doelbedrag</param>
            /// <param name="snelheid">De snelheid</param>
            /// <param name="penalty">De penalty in euro/seconde</param>
            /// <returns>Het object Collector</returns>
            public static Collector GetInstance(double doelBedrag, double snelheid, double penalty)
            {
                return singleton ?? (singleton = new Collector(doelBedrag, snelheid, penalty));
            }

            public void GeefWinkelwagen(Winkelwagen wagen)
            {
                double tijd = snelheid/bestewagen.Afstand;
                tijd += (Math.Abs(doelBedrag - bestewagen.Totaalprijs) / penalty);

                double alternatieveTijd = snelheid/wagen.Afstand;
                alternatieveTijd += (Math.Abs(doelBedrag - wagen.Totaalprijs) / penalty);

                if (alternatieveTijd < tijd)
                    this.tijd = alternatieveTijd;
                    bestewagen = wagen;
            }
        }

        public Winkelwagen BesteBedragAlgoritme(double doelBedrag, double snelheid, double penalty, POI begin)
        {
            Winkelwagen wagen = new Winkelwagen(begin);
            Collector col = Collector.GetInstance(wagen, doelBedrag, snelheid, penalty);
            Bereken(wagen, doelBedrag, snelheid, penalty);            
            return col.Bestewagen;
        }

        private void Bereken(Winkelwagen wagen, double doelBedrag, double snelheid, double penalty)
        {
            Collector collector = Collector.GetInstance(doelBedrag, snelheid, penalty);

            if(penalty > collector.Tijd || wagen.Vol)
            {
                collector.GeefWinkelwagen(wagen);
            }

            else
            {
                List<Winkelwagen> wagens = new List<Winkelwagen>();
                int aantal = wagen.Plaats.Artikelen.Count;
                for (int i = 0; i < Math.Pow(2, aantal); i++)
                {
                    wagens.Add(new Winkelwagen(wagen.Plaats));
                    int temp = i;
                    for (int j = 0; j < aantal; j++)
                    {
                        if (temp % 2 == 1)
                        {
                            wagens[i].LaadArtikelIn(wagen.Plaats.Artikelen[j]);
                        }
                        if (temp != 0)
                        {
                            temp = temp / 2;
                        }
                    }
                    collector.GeefWinkelwagen(wagens[i]);
                    for (int j = 0; j < wagen.Plaats.Kinderen.Count; j++)
                    {
                        wagens[i].Afstand += wagen.Plaats.Kinderen[j].Afstand;
                        AbstracteLaag ab = AbstracteLaag.GetInstance();
                        wagens[i].Plaats = ab.PuntenVanInteresse[wagen.Plaats.Kinderen[j].Nummer];
                        Bereken(wagens[i], doelBedrag, snelheid, penalty);
                    }
                }
            }
        }

        public override Winkelwagen AlgoritmeUitvoeren()
        {
            return new Winkelwagen(new POI(0,0,0));
        }

        public override void StopAlgoritme()
        {
            throw new NotImplementedException();
        }
    }
    #endregion

    #region BesteBenaderingBedrag
    public class BesteBenaderingBedrag : Algoritme
    {
        public override Winkelwagen AlgoritmeUitvoeren()
        {
            throw new NotImplementedException();
        }

        public override void StopAlgoritme()
        {
            throw new NotImplementedException();
        }

        private Winkelwagen GeneratieAlgoritme(List<Winkelwagen> beginGeneratie, int generaties)
        {
            int teller = 0;
            do
            {
                beginGeneratie = bepaalFitheid(beginGeneratie);
                beginGeneratie = beste10(beginGeneratie);
                beginGeneratie = maakGeneratie(beginGeneratie);
                teller++;
            } while (teller < generaties);
            return beginGeneratie[0];
        }

        private List<Winkelwagen> maakGeneratie(List<Winkelwagen> oplossingen)
        {
            List<Winkelwagen> generatie = new List<Winkelwagen>();
            generatie.Add(oplossingen[0]);
            Winkelwagen dummy = new Winkelwagen(generatie[0].AfgelegdeWeg[0]);

            return generatie;
        }  
        private List<Winkelwagen> beste10(List<Winkelwagen> beginGeneratie)
        {
            List<Winkelwagen> waarden = new List<Winkelwagen>();
            if (beginGeneratie.Count < 1024)
            {
                beginGeneratie = MergeSort(beginGeneratie);
                for (int i = 0; i < 10; i++)
                {
                    waarden.Add(beginGeneratie[i]);
                }
            }
            else
            {
                beginGeneratie = selectionSort(beginGeneratie);
                for (int i = 0; i < 10; i++)
                {
                    waarden.Add(beginGeneratie[i]);
                }
            }
            return waarden;
        }
        private List<Winkelwagen> bepaalFitheid(List<Winkelwagen> oplossingen)
        {
            for (int i = 0; i < oplossingen.Count; i++)
            {
                oplossingen[i].Fitheid = oplossingen[i].Totaalprijs * 100;
            }
            return oplossingen;
        }

        private List<Winkelwagen> MergeSort(List<Winkelwagen> array)
        {
            if (array.Count <= 1)
            {
                return array;
            }
            else
            {
                int midden = array.Count / 2;
                // Array B wordt gemaakt
                List<Winkelwagen> B = new List<Winkelwagen>();
                // Array A wordt nu gemaakt
                List<Winkelwagen> A = new List<Winkelwagen>();
                int teller;
                // De array's A en B krijgen elementen van het Array
                for (teller = 0; teller < midden; teller++)
                {
                    A.Add(array[teller]);
                }
                for (; teller < array.Count; teller++)
                {
                    B.Add(array[teller]);
                }
                A = MergeSort(A);
                B = MergeSort(B);
                array = Merge(A, B, array);
            }
            return array;
        }
        private List<Winkelwagen> Merge(List<Winkelwagen> A, List<Winkelwagen> B, List<Winkelwagen> C)
        {
            int i = 0, j = 0, k = 0;
            while (i < A.Count && j < B.Count)
            {
                if (A[i].Fitheid < B[j].Fitheid)
                {
                    C[k] = A[i];
                    i++;
                }
                else
                {
                    C[k] = B[j];
                    j++;
                }
                k++;
            }
            if (i == A.Count)
            {
                //kopieert de rest van B naar C
                while (j < B.Count)
                {
                    C[k] = B[j];
                    j++;
                    k++;
                }
            }
            else
            {
                //kopieert de rest van A naar C
                while (i < A.Count)
                {
                    C[k] = A[i];
                    i++;
                    k++;
                }
            }
            return C;
        }

        /// <summary>
        /// Algor1 selection
        /// </summary>
        /// <param name="ongesorteerd"></param>
        /// <returns></returns>
        public List<Winkelwagen> selectionSort(List<Winkelwagen> ongesorteerd)
        {
            for (int i = 0; i < ongesorteerd.Count && i < 10; i++)
            {
                int minIndex = i;
                for (int j = i + 1; j < ongesorteerd.Count; j++)
                {
                    if (ongesorteerd[j].Fitheid < ongesorteerd[minIndex].Fitheid)
                    {
                        minIndex = j;
                    }
                }
                Winkelwagen hulp = ongesorteerd[i];
                ongesorteerd[i] = ongesorteerd[minIndex];
                ongesorteerd[minIndex] = hulp;
            }
            return ongesorteerd;
        }
    }

   


    #endregion
}
